/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2008-2011, Salzburg Research. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * - Neither the name of the KiWi Project nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Contributor(s):
 * sschaffe
 *
 * File Info:
 * User: sschaffe
 * Date: 2011/01/04
 * Time: 14:00
 *
 * Project: KiWi2, kiwi-core module
 */

package kiwi.core.services.triplestore;

import kiwi.core.api.config.ConfigurationService;
import kiwi.core.api.persistence.PersistenceService;
import kiwi.core.api.transaction.TransactionData;
import kiwi.core.api.transaction.TransactionService;
import kiwi.core.api.triplestore.KnowledgeSpaceService;
import kiwi.core.api.triplestore.LiteralService;
import kiwi.core.api.triplestore.ResourceService;
import kiwi.core.api.triplestore.TripleStore;
import kiwi.core.exception.NamespaceResolvingException;
import kiwi.core.exception.ResourceResolvingException;
import kiwi.core.exception.transaction.TransactionNotActiveException;
import kiwi.core.model.Constants;
import kiwi.core.model.rdf.*;
import kiwi.core.model.user.KiWiUser;
import kiwi.core.qualifiers.cache.KiWiCache;
import kiwi.core.qualifiers.kspace.CurrentKnowledgeSpace;
import kiwi.core.qualifiers.user.CurrentUser;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.slf4j.Logger;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.*;

/**
 * TODO: Implement with methods from resource_service.txt
 * <p/>
 * User: sschaffe
 */
@SuppressWarnings({"JpaQueryApiInspection"})
@Named("kiwi.core.resourceService")
@ApplicationScoped
public class ResourceServiceImpl implements ResourceService, Serializable {



    @Inject
    private Logger log;


    @Inject
    private TripleStore tripleStore;

    @Inject
    private LiteralService literalService;

    @Inject
    private KnowledgeSpaceService knowledgeSpaceService;


    @Inject
    private PersistenceService persistenceService;

    /**
     * The transaction service is used for managing transaction-related information.
     */
    @Inject
    private TransactionService transactionService;

    /**
     * The KiWi Configuration Service for looking up configuration details
     */
    @Inject
    private ConfigurationService configurationService;


    @Inject @KiWiCache("uri-node-cache")
    private Cache uriNodeCache;

    @Inject @KiWiCache("anon-node-cache")
    private Cache anonNodeCache;

    /** the current user, to add as creator to newly created resources */
    @Inject @CurrentUser
    private KiWiUser currentUser;


    @Inject @CurrentKnowledgeSpace
    private KiWiUriResource currentKnowledgeSpace;


    public ResourceServiceImpl() {
    }


    /**
     * Note: if resource in the database and context != resource.context it will give a IllegalArgumentException
     */
    private KiWiResource createResourceInternal(KiWiResource resource, String title, KiWiUriResource context) {

        if(!transactionService.getTransaction().isActive()) {
            log.error("cannot create resource {}, the transaction {} is not active",resource,transactionService.getTransaction());
            throw new TransactionNotActiveException("cannot create resource "+resource+", the transaction "+transactionService.getTransaction()+" is not active");
        }

        if(title != null) {
            resource.setTitle(title);
        }

        if(currentUser != null) {
            resource.setCreator(currentUser);
        }

        if(resource.isUriResource()) {
            KiWiUriResource u = (KiWiUriResource)resource;

            // special case: we create the default knowledge space itself and his context graphcontent
            if(u.getUri().equals(configurationService.getBaseUri() + "knowledgespace/system")) {
                resource.setContext(u);
            } else if(u.getUri().equals(configurationService.getBaseUri() + "knowledgespace/default")) {
                resource.setContext(knowledgeSpaceService.getSystemKnowledgeSpace());
            }
        }

        if(resource.getContext() == null && context == null) {
            resource.setContext(knowledgeSpaceService.getDefaultKnowledgeSpace());
        } else if(context != null) {
            resource.setContext(context);
        }


        resource.setCreatorTransactionId(transactionService.getTransaction().getId());
        transactionService.getTransaction().getData().getAddedNodes().add(resource);

        log.debug("creating new resource {} (active transaction: {})",resource,transactionService.getTransaction());


        return resource;
    }


    /**
     * Creates a new KiWiUriResource with a random URI that maps back to this instance of the KiWi 2 system.
     *
     * @return
     */
    @Override
    public KiWiUriResource createUriResource() {
        return createUriResource(configurationService.getBaseUri()+"resource/"+UUID.randomUUID());
    }

    @Override
    public KiWiUriResource createUriResource(KiWiUriResource context) {
        return createUriResource(configurationService.getBaseUri()+"resource/"+UUID.randomUUID(),null);
    }

    /**
     * Create a new KiWiUriResource in this knowledge space using the provided uri. Note that resources are not
     * stored unless they are part of a triple.
     * @param uri
     * @return
     */
    @Override
    public synchronized KiWiUriResource createUriResource(String uri, KiWiUriResource context) {

        KiWiUriResource result = getUriResource(uri);

        if(result == null) {
            String label = null;
            if(uri.lastIndexOf("#") > 0) {
                label = uri.substring(uri.lastIndexOf("#")+1);
            } else {
                label = uri.substring(uri.lastIndexOf("/")+1);
            }
            result = (KiWiUriResource) createResourceInternal(new KiWiUriResource(uri), label, context);
            uriNodeCache.put(new Element(uri,result));
        } else if(result.getId() == null && !transactionService.getTransaction().getId().equals(result.getCreatorTransactionId())) {
            // the resource is not yet persisted, but it has been created by a different transaction, so we need to tell the current transaction
            // that it has to wait for the other transaction to commit before it can do so itself
            transactionService.addTransactionDependency(result.getCreatorTransactionId());

        }

        return result;
    }

    @Override
    public synchronized KiWiUriResource createUriResource(String uri) {

        return createUriResource(uri, null);
    }


    /**
     * Create a new KiWiAnonResource using a new unique identifier.  Note that resources are not stored unless
     * they are part of a triple.
     *
     * @return
     */
    @Override
    public KiWiAnonResource createAnonResource() {
        return createAnonResource((KiWiUriResource) null);
    }

    @Override
    public KiWiAnonResource createAnonResource(KiWiUriResource context) {
        return createAnonResource(Long.toHexString(System.currentTimeMillis())+Integer.toHexString((int) (Math.random()*1000)), context );
    }

    /**
     * Create a new KiWiAnonResource using the provided identifier. If the id is already used, will return the
     * object associated with this id. Note that resources are not stored unless they are part of a triple.
     * Note: if resource in the database and context != resource.context it will give a IllegalArgumentException
     *
     *
     * @return
     */
    @Override
    public synchronized KiWiAnonResource createAnonResource(String id, KiWiUriResource context) {
        KiWiAnonResource result;


        if(anonNodeCache.get(id) != null) {
            result = (KiWiAnonResource) anonNodeCache.get(id).getObjectValue();
            if(result.getId() == null && !transactionService.getTransaction().getId().equals(result.getCreatorTransactionId())) {
                // the resource is not yet persisted, but it has been created by a different transaction, so we need to tell the current transaction
                // that it has to wait for the other transaction to commit before it can do so itself
                transactionService.addTransactionDependency(result.getCreatorTransactionId());
            }
        } else {
            EntityManager entityManager = persistenceService.getEntityManager();
            EntityTransaction tx = entityManager.getTransaction();

            tx.begin();


            Query q = entityManager.createNamedQuery("tripleStore.resourceByAnonId");
            q.setParameter("anonId", id);
            q.setHint("org.hibernate.cacheable", true);
            q.setMaxResults(1);

            try {
                result = (KiWiAnonResource) q.getSingleResult();
                if(context != null && !result.getContext().getUri().equals(context.getUri())) {
                    throw new IllegalArgumentException("context of resource "
                            + result.getContext().getUri()+" is a other as the given context " +
                    context.getUri());
                }

            } catch(NoResultException ex) {
                result = (KiWiAnonResource) createResourceInternal(new KiWiAnonResource(id),id,context);
            }
            anonNodeCache.put(new Element(result.getCacheKey(),result));

            tx.commit();
            persistenceService.closeEntityManager(entityManager);
        }
        return result;


    }

    @Override
    public synchronized KiWiAnonResource createAnonResource(String id) {
        
        return createAnonResource(id, null);
    }


    /**
     * Remove the resource given as argument. The resource is added to the transaction data and marked as
     * deleted when the transaction commits. Note: it remove only the triples of the given context. If context
     * == null it will remove the resource and triple of the resource.
     *
     * @param r
     */
    @Override
    public void removeResource(KiWiResource r, KiWiUriResource context) {
        if(r != null) {

            // remove all triples this resource is used in
            for(KiWiTriple triple : listIncoming(r, context)) {
                tripleStore.removeTriple(triple);
            }
            for(KiWiTriple triple : listOutgoing(r, context)) {
                tripleStore.removeTriple(triple);

                // also delete dependent literals
                if(triple.getObject().isLiteral()) {
                    triple.getObject().setDeleted(true);
                    transactionService.getTransaction().getData().getRemovedNodes().add(triple.getObject());
                }
            }


            if(context == null) {
                r.setDeleted(true);
                uriNodeCache.remove(r.getCacheKey());

                transactionService.getTransaction().getData().getRemovedNodes().add(r);
            }
        }
    }

    @Override
    public void removeResource(KiWiResource r) {
        removeResource(r, null);
    }

    /**
     * Retrieve the KiWiUriResource with the given URI if it exists, or return null if it doesn't exist.
     *
     * @param uri
     * @return
     */
    @Override
    public KiWiUriResource getUriResource(String uri) {
        KiWiUriResource result;

        if(uriNodeCache.get(uri) != null) {
            result = (KiWiUriResource) uriNodeCache.get(uri).getObjectValue();
        } else {
            EntityManager entityManager = persistenceService.getEntityManager();
            EntityTransaction tx = entityManager.getTransaction();

            tx.begin();
            Query q = entityManager.createNamedQuery("tripleStore.resourceByUri");
            q.setParameter("uri",uri);
            q.setHint("org.hibernate.cacheable", true);
            q.setMaxResults(1);
            try {
                result = (KiWiUriResource) q.getSingleResult();
                uriNodeCache.put(new Element(uri,result));
            } catch(NoResultException ex) {
                result = null;
                uriNodeCache.put(new Element(uri,result));
            }
            tx.commit();
            persistenceService.closeEntityManager(entityManager);

        }
        return result;
    }

    /**
     * Retrieve the KiWiAnonResource with the given ID if it exists, or return null if it doesn't exist.
     *
     * @param id
     * @return
     */
    @Override
    public KiWiAnonResource getAnonResource(String id) {
        KiWiAnonResource result;


        if(anonNodeCache.get(id) != null) {
            result = (KiWiAnonResource) anonNodeCache.get(id).getObjectValue();
        } else {
            EntityManager entityManager = persistenceService.getEntityManager();
            EntityTransaction tx = entityManager.getTransaction();

            tx.begin();

            Query q = entityManager.createNamedQuery("tripleStore.resourceByAnonId");
            q.setParameter("anonId",id);
            q.setHint("org.hibernate.cacheable", true);
            q.setMaxResults(1);
            try {
                result = (KiWiAnonResource) q.getSingleResult();
                anonNodeCache.put(new Element(id,result));
            } catch(NoResultException ex) {
                result = null;
            }
            tx.commit();
            persistenceService.closeEntityManager(entityManager);
        }
        return result;
    }

    /**
     * List all resources contained in the KiWi System, regardless of knowledge space or type.
     *
     * @return
     */
    @Override
    public Collection<KiWiResource> listResources() {
        return listResources(0,0);
    }

    @Override
    public Collection<KiWiResource> listResources(int offset, int limit) {
        return listResources(offset,limit, null);
    }

    /**
     * List all resources contained in the KiWi System, regardless of knowledge space or type. Optionally supports
     * paging if limit and offset are given (> 0).
     *
     * @return
     */
    @Override
    public Collection<KiWiResource> listResources(int offset, int limit, KiWiUriResource context) {
        Query q = null;
        EntityManager entityManager = persistenceService.getEntityManager();
        EntityTransaction tx = entityManager.getTransaction();

        tx.begin();

        if(context == null) {
            q = entityManager.createNamedQuery("contentItemService.list");
            q.setHint("org.hibernate.cacheable", true);

            if(offset > 0) {
                q.setFirstResult(offset);
            }
            if(limit > 0) {
                q.setMaxResults(limit);
            }
        } else {
            q = entityManager.createNamedQuery("contentItemService.bySpace");
            q.setParameter("space", context);
            q.setHint("org.hibernate.cacheable", true);

            if(offset > 0) {
                q.setFirstResult(offset);
            }
            if(limit > 0) {
                q.setMaxResults(limit);
            }
        }

        Set<KiWiResource> result;

        try { // If context is not persist in the database. just in the cache
            result = new HashSet(((Collection<KiWiResource>) q.getResultList()));
        } catch(IllegalStateException e) {
            result = new HashSet();
        }
        tx.commit();
        persistenceService.closeEntityManager(entityManager);

        // if the transaction is active, we also need to query the data contained in it; note that this is not
        // necessary for transactions that are committed already (e.g. in the @AfterCommit event) because then
        // the data will already be contained in the database query
        if(transactionService.getTransaction().isActive() &&
           ( transactionService.getTransaction().getData().getAddedNodes().size() > 0 ||
             transactionService.getTransaction().getData().getRemovedNodes().size() > 0  ) ) {

            long start = System.currentTimeMillis();

            // now look for all matching triples in the current transaction
            TransactionData data = transactionService.getTransaction().getData();

            // perform cleanup first (cf. TransactionServiceImpl)
            HashSet<KiWiNode> addedResources = new HashSet<KiWiNode>(data.getAddedNodes());
            HashSet<KiWiNode> removedResources = new HashSet<KiWiNode>(data.getRemovedNodes());
            if(addedResources.size() > 0 && removedResources.size() > 0) {
                for(KiWiNode added : new HashSet<KiWiNode>(addedResources)) {
                    if(removedResources.remove(added)) {
                        addedResources.remove(added);
                    }
                }
            }

            // update the result list with results from transaction

            // remove all triples from result that have been removed in transaction; we simply iterate over all
            // removed nodes and remove them from the result set
            if(removedResources.size() > 0) {
                for(KiWiNode triple : removedResources) {
                    result.remove(triple);
                }
            }

            // filter the added nodes based on the search criteria and add them to the result
            if(addedResources.size() > 0) {

                for(KiWiNode node : addedResources) {

                    KiWiResource contextResource = node.getContext();

                    if ((node.isAnonymousResource() || node.isUriResource()) && (context == null ||
                            contextResource.toString().equals(context.toString()))) {

                        result.add((KiWiResource) node);
                    }

                }
            }
        }

        return result;
    }


    /**
     * List all resources of a specific type in the KiWi system.
     *
     * @param type the type of the resources to list
     * @return
     */
    @Override
    public Collection<KiWiResource> listResources(KiWiResource type) {
        KiWiUriResource rdf_type = createUriResource(Constants.NS_RDF+"type");

        Set<KiWiTriple> triples = tripleStore.listTriples(null,rdf_type,type,null,true);
        Set<KiWiResource> resources = new HashSet<KiWiResource>();
        for(KiWiTriple triple : triples) {
            resources.add(triple.getSubject());
        }

        return resources;
    }

    /**
     * List all resources contained in a certain knowledge space in the KiWi system.
     * Look by Resource and Triples where the resource is the subject.
     *
     * @param context the resource identifying the knowledge space
     * @return
     */
    @Override
    public Collection<KiWiResource> listResourcesBySpace(KiWiUriResource context) {

        Set<KiWiTriple> triples = tripleStore.listTriples(null, null, null, context, true);
        Set<KiWiResource> result = new HashSet<KiWiResource>();
        for(KiWiTriple triple : triples) {
            result.add(triple.getSubject());
        }

        result.addAll(this.listResources(0,0,context));

        return result;
     }

    /**
     * List all resources of a specific type contained in a certain knowledge space in the KiWi system
     *
     * @param context the resource identifying the knowledge space
     * @param type  the type of the resources to list
     * @return
     */
    @Override
    public Collection<KiWiResource> listResourcesBySpace(KiWiUriResource context, KiWiResource type) {
        KiWiUriResource rdf_type = createUriResource(Constants.NS_RDF + "type");

        Set<KiWiTriple> triples = tripleStore.listTriples(null,rdf_type,type,context,true);
        Set<KiWiResource> resources = new HashSet<KiWiResource>();
        for(KiWiTriple triple : triples) {
            resources.add(triple.getSubject());
        }

        return resources;
    }

    /**
     * List all resources that have a specific property set.
     *
     * @param propertyUri
     * @param value
     * @return
     */
    @Override
    public Collection<KiWiResource> listResourcesByProperty(String propertyUri, KiWiNode value, KiWiUriResource context) {
        KiWiUriResource property = createUriResource(propertyUri);

        Set<KiWiTriple> triples = tripleStore.listTriples(null,property,value,context,true);
        Set<KiWiResource> resources = new HashSet<KiWiResource>();
        for(KiWiTriple triple : triples) {
            resources.add(triple.getSubject());
        }

        return resources;
    }

    @Override
    public Collection<KiWiResource> listResourcesByProperty(String propertyUri, KiWiNode value) {

        return listResourcesByProperty(propertyUri, value, null);
    }

    /**
     * List all resources that have a specific property set to the given value
     *
     * @param propertyUri
     * @param value       the literal value to query for
     * @return
     */
    @Override
    public Collection<KiWiResource> listResourcesByProperty(String propertyUri, String value, KiWiUriResource context) {
        KiWiUriResource property = createUriResource(propertyUri);

        Set<KiWiTriple> triples = tripleStore.listTriples(null,property,context,null,true);
        Set<KiWiResource> resources = new HashSet<KiWiResource>();
        for(KiWiTriple triple : triples) {
            if(triple.getObject().isLiteral() && ((KiWiLiteral)triple.getObject()).getContent().equals(value)) {
                resources.add(triple.getSubject());
            }
        }

        return resources;
    }

    @Override
    public Collection<KiWiResource> listResourcesByProperty(String propertyUri, String value) {
        return listResourcesByProperty(propertyUri, value, null);
    }

    /**
     * Generic method to query for literal values related to this resource with the property
     * identified by "propLabel" (SeRQL/SPARQL short or long notation). Returns only literal
     * values for which no language has been assigned.
     *
     * @param propLabel
     * @return
     */
    @Override
    public String getProperty(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        return getProperty(r,propLabel,(Locale) null, context);
    }

    /**
     * Generic method to query for literal values related to this resource with the property
     * identified by "propLabel" (SeRQL/SPARQL short or long notation). Returns only literal
     * values for which no language has been assigned.
     *
     * @param propLabel
     * @return
     */
    @Override
    public String getProperty(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        return getProperty(r,propLabel,null, null);
    }

    @Override
    public String getProperty(KiWiResource r, String propLabel, Locale loc, KiWiUriResource context)
            throws NamespaceResolvingException, ResourceResolvingException {
        KiWiLiteral l;
        try {
            l = getLiteral(r,propLabel,loc, context);
            if(l == null) {
                return null;
            } else {
                return l.getContent();
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Generic method to query for literal values related to this resource with the property
     * identified by "propLabel" (SeRQL/SPARQL short or long notation) and the given locale.
     *
     * @param propLabel label of the property; either RDF short form (e.g. "foaf:mbox") or long form (e.g. <http://xmlns.com/foaf/0.1/mbox>)
     * @param loc
     * @return
     */
    @Override
    public String getProperty(KiWiResource r, String propLabel, Locale loc) throws NamespaceResolvingException, ResourceResolvingException {
        return getProperty(r, propLabel, loc, null);
    }

    /**
     * Generic method to query for literal values related to this resource with the property
     * identified by "propLabel" (SeRQL/SPARQL short or long notation) and the given locale.
     *
     * @param propLabel label of the property; either RDF short form (e.g. "foaf:mbox") or long form (e.g. <http://xmlns.com/foaf/0.1/mbox>)
     * @return
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public Iterable<String> getProperties(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        return getProperties(r,propLabel,null, context);
    }

    /**
     * Generic method to query for literal values related to this resource with the property
     * identified by "propLabel" (SeRQL/SPARQL short or long notation) and the given locale.
     *
     * @param propLabel label of the property; either RDF short form (e.g. "foaf:mbox") or long form (e.g. <http://xmlns.com/foaf/0.1/mbox>)
     * @return
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public Iterable<String> getProperties(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        return getProperties(r,propLabel,null, (KiWiUriResource) null);
    }

    @Override
    public Iterable<String> getProperties(KiWiResource r, String propLabel, Locale loc, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        List<KiWiLiteral> props = listLiterals(r,propLabel, loc,context);
        List<String> results = new ArrayList<String>(props.size());

        for(KiWiLiteral l : props) {
            results.add(l.getContent());
        }

        return results;
    }

    /**
     * Generic method to query for literal values related to this resource with the property
     * identified by "propLabel" (SeRQL/SPARQL short or long notation) and the given locale.
     *
     * @param propLabel label of the property; either RDF short form (e.g. "foaf:mbox") or long
     *                  form (e.g. <http://xmlns.com/foaf/0.1/mbox>)
     * @param loc
     * @return
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public Iterable<String> getProperties(KiWiResource r, String propLabel, Locale loc) throws NamespaceResolvingException, ResourceResolvingException {
        return getProperties(r, propLabel, loc, null);
    }

    /**
     * Generic method to set the literal value of a property of this resource to the provided
     * value without setting a language.
     *
     * @param propLabel the SeRQL or SPARQL short or long notation for the property
     * @param propValue the String value of this property
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public void setProperty(KiWiResource r, String propLabel, String propValue, KiWiUriResource context) throws NamespaceResolvingException {
        setProperty(r,propLabel,propValue,null, context);
    }

    /**
     * Generic method to set the literal value of a property of this resource to the provided
     * value without setting a language.
     *
     * @param propLabel the SeRQL or SPARQL short or long notation for the property
     * @param propValue the String value of this property
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public void setProperty(KiWiResource r, String propLabel, String propValue) throws NamespaceResolvingException {
        setProperty(r,propLabel,propValue,(KiWiUriResource)null);
    }

    @Override
    public <T> void setProperty(KiWiResource r, String propLabel, T propValue, Locale loc) throws NamespaceResolvingException {

        setProperty(r,propLabel,propValue,null, null);
    }

    /**
     * Generic method to set the literal value of a property of this resource to the provided
     * value in the provided language.
     *
     * @param propLabel the SeRQL or SPARQL short or long notation for the property
     * @param propValue the String value of this property
     * @param loc       the Locale representing the language of this property
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public <T> void setProperty(KiWiResource r, String propLabel, T propValue, Locale loc, KiWiUriResource context) throws NamespaceResolvingException {

//        String key = propLabel + (loc == null?"":"@"+loc.getLanguage());
        if(context == null) {
            context = knowledgeSpaceService.getDefaultKnowledgeSpace();
        }

        if(propValue != null) {
//            // quick check whether the cached value is the same as the passed value; in this case we
//            // do nothing because no update is needed
//            if(properties.get(key) != null &&
//               properties.get(key).getContent().equals(propValue)) {
//                return;
//            }

            // remove previous property setting
            removeProperty(r,propLabel,loc,context);

            String prop_uri = _resolvePropLabel(propLabel);

            // then set the new property value
            //KiWiUriResource propResource = tripleStore.createUriResource(prop_uri);
            KiWiLiteral value = literalService.createLiteral(propValue, loc, null);
            tripleStore.createTriple(r, createUriResource(prop_uri), value, context);

//            // and update properties cache
//            properties.put(key, value);
//            undef_properties.put(key, Boolean.FALSE);
        } else {
            removeProperty(r, propLabel,loc,context);

        }
    }

    /**
     * Remove a property from the KiWiResource.
     *
     * @param propLabel the property label in SeRQL syntax to remove
     * @return true if the property existed and was removed
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public boolean removeProperty(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException {
        return removeProperty(r,propLabel, (Locale) null, context);
    }

    @Override
    public boolean removeProperty(KiWiResource r, String propLabel) throws NamespaceResolvingException {
        return removeProperty(r,propLabel,(Locale) null);
    }

    @Override
    public boolean removeProperty(KiWiResource r, String propLabel, Locale loc) throws NamespaceResolvingException {

        return removeProperty(r, propLabel, loc, null);
    }

    /**
     * Remove a property from the KiWiResource.
     *
     * @param propLabel the property label in SeRQL syntax to remove
     * @param loc       the locale of the property to remove
     * @return true if the property existed and was removed
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public boolean removeProperty(KiWiResource r, String propLabel, Locale loc, KiWiUriResource context) throws NamespaceResolvingException {
        String uri = _resolvePropLabel(propLabel);
        KiWiUriResource property = getUriResource(uri);

        if(property != null) {
            // look up triple that corresponds to property
            Set<KiWiTriple> triples = tripleStore.listTriples(r,property,null,context,false);

            // filter by locale
            if(loc != null) {
                for(Iterator<KiWiTriple> it = triples.iterator(); it.hasNext(); ) {
                    KiWiTriple t = it.next();
                    if(!t.getObject().isLiteral() || !((KiWiLiteral)t.getObject()).getLanguage().equals(loc)) {
                        it.remove();
                    }
                }
            }

            // if triple exists, call TripleStore.remove on it
            if(triples.size() > 0) {
                for(KiWiTriple triple : triples) {
                    tripleStore.removeTriple(triple);
                }
                return true;
            }
        }
        return false;
    }

    /**
     * List all outgoing edges from this resource to other resources. Shortcut for listOutgoing(null).
     *
     * @return all outgoing edges from this resource
     */
    @Override
    public List<KiWiTriple> listOutgoing(KiWiResource r) {
        return listOutgoing(r, (KiWiUriResource) null);
    }

    /**
     * List all outgoing edges from this resource to other resources. Shortcut for listOutgoing(null).
     *
     * @return all outgoing edges from this resource
     */
    @Override
    public List<KiWiTriple> listOutgoing(KiWiResource r, KiWiUriResource context) {
        try {
            return listOutgoing(r,null,context);
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
            return Collections.emptyList();
        } catch (ResourceResolvingException e) {
            // cannot happen in this case
            return Collections.emptyList();
        }
    }


    /**
     * List outgoing edges from this resource to other resources, using the property label passed
     * as argument. If limit is bigger than 0, then a maximum of limit triples will be returned.
     * Otherwise, all triples will be returned.
     * <p/>
     * The parameter propLabel is in the form of a SeRQL or SPARQL id. It can take one of the following
     * values:
     * <ul>
     * <li>a URI enclosed in &lt; &gt, e.g. &lt;http://www.example.com/myProp&gt;</li>
     * <li>a uri prefix, followed by a colon and the property name, e.g. ex:myProp</li>
     * <li>the value "null", in which case all outgoing edges are listed regardless of their label
     * (wildcard)</li>
     * </ul>
     * The result will be an iterable that allows to iterate over KiWiTriples.
     *
     * @param propLabel the label of the property to be queried, or null for wildcard
     * @return an iterable over the KiWiTriples that are outgoing edges of this resource
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public List<KiWiTriple> listOutgoing(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        return this.listOutgoing(r, propLabel, null);
    }

    @Override
    public List<KiWiTriple> listOutgoing(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        KiWiUriResource property = null;

        if(propLabel != null) {
            property = getUriResource(_resolvePropLabel(propLabel));

            if(property == null) {
                throw new ResourceResolvingException("the resource with label "+propLabel+" could not be resolved");
            }
        }

        return new LinkedList<KiWiTriple>(tripleStore.listTriples(r,property,null,context,true));
    }

    /**
     * List the objects that are related to this resource through a certain property
     *
     * @return
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public Iterable<KiWiNode> listOutgoingNodes(KiWiResource r, String propLabel, KiWiUriResource context)
            throws NamespaceResolvingException, ResourceResolvingException {

        Collection<KiWiTriple> out = listOutgoing(r,propLabel,context);
        List<KiWiNode> result = new ArrayList<KiWiNode>(out.size());
        for(KiWiTriple t : out) {
            result.add(t.getObject());
        }
        return result;
    }


    /**
     * List the objects that are related to this resource through a certain property
     *
     * @return
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public Iterable<KiWiNode> listOutgoingNodes(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        return listOutgoingNodes(r, propLabel, null);
    }

    /**
     * Set the edge identified by propLabel to the node passed as target. All existing edges with propLabel will
     * be deleted.
     *
     * @param propLabel
     * @param target
     * @param <C>
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public <C extends KiWiNode> void setOutgoingNode(KiWiResource r, String propLabel, C target, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {

        if(context == null) {
            context = knowledgeSpaceService.getDefaultKnowledgeSpace();
        }

        // remove all existing triples
        for( KiWiTriple t : listOutgoing(r,propLabel,context)) {
            tripleStore.removeTriple(t);
        }
        KiWiUriResource propResource = createUriResource(_resolvePropLabel(propLabel));
        tripleStore.createTriple(r, propResource, target, context);
    }

    public <C extends KiWiNode> void setOutgoingNode(KiWiResource r, String propLabel, C target) throws NamespaceResolvingException, ResourceResolvingException {

        setOutgoingNode(r,propLabel,target,knowledgeSpaceService.getDefaultKnowledgeSpace());
    }

    /**
     * Add an edge between the resource r and the resource target, using propLabel as identifier
     *
     * @param r
     * @param propLabel
     * @param target
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public void addOutgoingNode(KiWiResource r, String propLabel, KiWiNode target) throws NamespaceResolvingException {
        addOutgoingNode(r, propLabel, target,null);
    }

    @Override
    public void addOutgoingNode(KiWiResource r, KiWiUriResource prop, KiWiNode target) throws NamespaceResolvingException {
        addOutgoingNode(r, prop, target, null);
    }

    @Override
    public void removeOutgoingNode(KiWiResource r, String propLabel, KiWiResource target) throws NamespaceResolvingException {
        removeOutgoingNode(r, propLabel, target, null);
    }

    @Override
    public void addOutgoingNode(KiWiResource r, String propLabel, KiWiNode target, KiWiUriResource context) throws NamespaceResolvingException {
        String property_uri = _resolvePropLabel(propLabel);
        tripleStore.createTriple(r, createUriResource(property_uri), target, context);
    }

    @Override
    public void addOutgoingNode(KiWiResource r, KiWiUriResource prop, KiWiNode target, KiWiUriResource context) throws NamespaceResolvingException {
        tripleStore.createTriple(r, prop, target,context);
    }

    @Override
    public void removeOutgoingNode(KiWiResource r, String propLabel, KiWiResource target, KiWiUriResource context) throws NamespaceResolvingException {
        String property_uri = _resolvePropLabel(propLabel);
        tripleStore.removeTriple(r, createUriResource(property_uri), target, context);
    }

    /**
     * List all incoming edges from other resources to this resource
     *
     * @return
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public List<KiWiTriple> listIncoming(KiWiResource r) {
        return listIncoming(r, (KiWiUriResource) null);
    }

    /**
     * List all incoming edges from other resources to this resource
     *
     * @return
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public List<KiWiTriple> listIncoming(KiWiResource r, KiWiUriResource context) {
        try {
            return listIncoming(r, null, context);
        } catch (NamespaceResolvingException e) {
            // does not happen
            log.error("caught exception that cannot occur, programming error");
            return Collections.emptyList();
        } catch (ResourceResolvingException e) {
            // does not happen
            log.error("caught exception that cannot occur, programming error");
            return Collections.emptyList();
        }
    }


    /**
     * List incoming edges from other resources to this resource, using the property label passed
     * as argument. If limit is bigger than 0, then a maximum of limit triples will be returned.
     * Otherwise, all triples will be returned.
     * <p/>
     * The parameter propLabel is in the form of a SeRQL or SPARQL id. It can take one of the following
     * values:
     * <ul>
     * <li>a URI enclosed in &lt; &gt, e.g. &lt;http://www.example.com/myProp&gt;</li>
     * <li>a uri prefix, followed by a colon and the property name, e.g. ex:myProp</li>
     * <li>the value "null", in which case all outgoing edges are listed regardless of their label
     * (wildcard)</li>
     * </ul>
     * The result will be an iterable that allows to iterate over KiWiTriples.
     *
     * @param propLabel the label of the property to be queried, or null for wildcard
     * @return an iterable over the KiWiTriples that are incoming edges of this resource
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public List<KiWiTriple> listIncoming(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        return listIncoming(r, propLabel, null);
    }

    @Override
    public List<KiWiTriple> listIncoming(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        KiWiUriResource property = null;

        if(propLabel != null) {
            property = getUriResource(_resolvePropLabel(propLabel));

            if(property == null) {
                throw new ResourceResolvingException("the resource with label "+propLabel+" could not be resolved");
            }
        }

        return new LinkedList<KiWiTriple>(tripleStore.listTriples(null,property,r,context,true));
    }

    /**
     * Return a list of nodes that are the sources for edges with propLabel that have this resource
     * as endpoint. This is mostly a convenience method that wraps listIncoming(propLabel).
     *
     * @param propLabel the label that all edges listed must have, or null for wildcard
     * @return a list of resources that are sources of edges that have this resource as endpoint
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public Iterable<KiWiResource> listIncomingNodes(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        Collection<KiWiTriple> in = listIncoming(r,propLabel,context);
        List<KiWiResource> result = new ArrayList<KiWiResource>(in.size());
        for(KiWiTriple t : in) {
            result.add(t.getSubject());
        }
        return result;
    }

    @Override
    public Iterable<KiWiResource> listIncomingNodes(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        return listIncomingNodes(r, propLabel, null);
    }

    @Override
    public void addIncomingNode(KiWiResource r, String propLabel, KiWiResource source, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {

        String p_uri = _resolvePropLabel(propLabel);
        KiWiUriResource propResource = createUriResource(p_uri);
        tripleStore.createTriple(source, propResource, r, null);
    }

    @Override
    public void addIncomingNode(KiWiResource r, String propLabel, KiWiResource source) throws NamespaceResolvingException, ResourceResolvingException {
        addIncomingNode(r,propLabel,source,null);
    }

    /**
     * Return the label of this resource in the language provided as parameter
     * <p/>
     * If no label is available for the given language, returns the identifier.
     *
     * @return
     */
    @Override
    public String getLabel(KiWiResource r) {
        return getLabel(r,(Locale)null);
    }
    @Override
    public String getLabel(KiWiResource r, KiWiUriResource context) {
        return getLabel(r,(Locale)null,context);
    }
    @Override
    public String getLabel(KiWiResource r, Locale loc) {
        return getLabel(r,loc,null);
    }

    /**
     * Return the label of this resource in the language provided as parameter
     * within the getTripleStore().knowledge space of this KiWiResource.
     * <p/>
     * If no label is available for the given language, returns the identifier.
     *
     * @param loc
     * @return
     */
    @Override
    public String getLabel(KiWiResource r, Locale loc, KiWiUriResource context) {
        String label = null;
        // check kiwi:title, rdfs:label, dc:title in this order ...
        String[] properties = { Constants.NS_KIWI_CORE+"title", Constants.NS_RDFS+"label", Constants.NS_DC+"title" };

        for(String property : properties) {
            try {
                label = getProperty(r,property,loc,context);
            } catch (NamespaceResolvingException e) {
                // should never happen
                log.error("could not resolve internal namespace; programming error!");
            } catch (ResourceResolvingException e) {
                // property not found - do nothing
            }
            if(label != null) {
                break;
            }
        }

        // no label available, take content item title
        if(label == null) {
            label = r.getTitle();
        }

        // still no label available, try to get last part from uri
        if(label == null && r.isUriResource()) {
            String uri = ((KiWiUriResource)r).getUri();
            if(uri.lastIndexOf("#") > 0) {
                label = uri.substring(uri.lastIndexOf("#")+1);
            } else {
                label = uri.substring(uri.lastIndexOf("/")+1);
            }
        } else if(label == null && r.isAnonymousResource()){
            label = ((KiWiAnonResource)r).getAnonId();
        }
        return label;
    }


    @Override
    public void setLabel(KiWiResource r, Locale loc, String label) {

        setLabel(r,loc,label,knowledgeSpaceService.getDefaultKnowledgeSpace());
    }

    /**
     * Set the rdfs:label of this KiWiResource in the configured getTripleStore().TripleStore
     * for the given Locale.
     *
     * @param loc
     * @param label
     * @throws kiwi.core.exception.NamespaceResolvingException
     *
     */
    @Override
    public void setLabel(KiWiResource r, Locale loc, String label, KiWiUriResource context) {
        try {
            setProperty(r,"<"+Constants.NS_RDFS+"label>", label, loc,context);
        } catch (NamespaceResolvingException e) {
            log.error("could not resolve internal namespace; programming error!");
        }
    }

    /**
     * Return the rdfs:comment for this resource for the given locale in the getTripleStore().
     * knowledge space.
     *
     * @param loc
     */
    @Override
    public String getComment(KiWiResource r, Locale loc, KiWiUriResource context) {
        try {
            return getProperty(r,"<"+Constants.NS_RDFS+"comment>",loc);
        } catch (NamespaceResolvingException e) {
            log.error("could not resolve internal namespace; programming error!");
            return null;
        } catch (ResourceResolvingException e) {
            log.error("rdfs:comment property does not exist");
            return null;
        }
    }

    @Override
    public String getComment(KiWiResource r, Locale loc) {
        return getComment(r,loc,null);
    }

    /**
     * Set the rdfs:comment for this resource
     *
     * @param loc
     * @param comment
     */
    @Override
    public void setComment(KiWiResource r, Locale loc, String comment, KiWiUriResource context) {
        try {
            setProperty(r,"<"+Constants.NS_RDFS+"comment>", comment, loc,context);
        } catch (NamespaceResolvingException e) {
            log.error("could not resolve internal namespace; programming error!");
        }
    }

    @Override
    public void setComment(KiWiResource r, Locale loc, String comment) {
        setComment(r,loc,comment,knowledgeSpaceService.getDefaultKnowledgeSpace());
    }

    /**
     * Return the list of types as KiWiResources that are associated with this resource using the
     * rdf:type RDF property.
     *
     * @return an iterable of KiWiResource instances that represent the RDF types of this resource
     */
    @Override
    public Collection<KiWiResource> getTypes(KiWiResource r, KiWiUriResource context) {
        KiWiUriResource rdf_type = getUriResource(Constants.NS_RDF+"type");

        if(rdf_type != null) {
            Set<KiWiResource> result = new HashSet<KiWiResource>();
            for(KiWiTriple triple : tripleStore.listTriples(r,rdf_type,null,null,true)) {
                result.add((KiWiResource)triple.getObject());
            }

            return result;
        } else {
            return Collections.EMPTY_LIST;
        }
    }

    @Override
    public Collection<KiWiResource> getTypes(KiWiResource r) {

        return getTypes(r,null);
    }



    @Override
    public void addType(KiWiResource r, KiWiResource type) {

        addType(r, type, null);
    }

    /**
     * Add a new type to the list of RDF types of this KiWiResource.
     *
     * @param type the type to add
     */
    @Override
    public void addType(KiWiResource r, KiWiResource type, KiWiUriResource context) {
        try {
            if(!hasType(r,(KiWiUriResource)type,context)) {
                addOutgoingNode(r,"<"+Constants.NS_RDF+"type>", type, context);
            }
        } catch (NamespaceResolvingException e) {
            log.error("could not resolve internal namespace; programming error!");
        }
    }

    /**
     * Remove one of the RDF types of this KiWiResource
     *
     * @param type a URI resource representing the type of this KiWiResource
     */
    @Override
    public void removeType(KiWiResource r, KiWiResource type) {
        removeType(r,type,null);
    }

    @Override
    public void removeType(KiWiResource r, KiWiResource type, KiWiUriResource context) {
        try {
            removeOutgoingNode(r,"<"+Constants.NS_RDF+"type>", type, context);
        } catch (NamespaceResolvingException e) {
            log.error("could not resolve internal namespace; programming error!");
        }
    }

    @Override
    public boolean hasType(KiWiResource r, String typeUri, KiWiUriResource context) {
        return hasType(r,getUriResource(typeUri), context);
    }

    @Override
    public boolean hasType(KiWiResource r, String typeUri) {
        return hasType(r,getUriResource(typeUri));
    }

    /**
     * Check whether this KiWiResource has a certain RDF type
     *
     * @param type the resource representing the type to check for
     * @return true if the type is in the list of RDF types of this resource, false otherwise
     */
    @Override
    public boolean hasType(KiWiResource r, KiWiUriResource type) {
        return hasType(r,type,null);
    }

    @Override
    public boolean hasType(KiWiResource r, KiWiUriResource type, KiWiUriResource context) {
        if(type != null) {
            KiWiUriResource rdf_type = getUriResource(Constants.NS_RDF+"type");

            if(rdf_type != null) {
                return tripleStore.listTriples(r,rdf_type,type,context,true).size() > 0;
            }
        }
        return false;
    }


    /**
     * Clear all caches that are currently active; used e.g. in case of a rollback to avoid
     * detached triples that are not really persisted.
     */
    public void clearCaches() {

        uriNodeCache.removeAll();
        anonNodeCache.removeAll();
    }

    /**
     * Generic method to query for literal values related to this resource with the property
     * identified by "propLabel" (SeRQL/SPARQL short or long notation) and the given locale.
     *
     * @param propLabel label of the property; either RDF short form (e.g. "foaf:mbox") or long form (e.g. <http://xmlns.com/foaf/0.1/mbox>)
     * @param loc
     * @return
     * @throws NamespaceResolvingException
     */
    private KiWiLiteral getLiteral(KiWiResource r, String propLabel, Locale loc) throws NamespaceResolvingException, ResourceResolvingException {
        return getLiteral(r, propLabel, loc, null);
    }

    private KiWiLiteral getLiteral(KiWiResource r, String propLabel, Locale loc, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        KiWiLiteral result = null;

        for(KiWiNode node : listOutgoingNodes(r,propLabel,context)) {
            if(node.isLiteral() &&
               ( (((KiWiLiteral)node).getLanguage() == null && loc == null) || (((KiWiLiteral)node).getLanguage() != null && ((KiWiLiteral)node).getLanguage().equals(loc)) ) ) {
                result = (KiWiLiteral)node;
                break;
            }
        }
        return result;
    }

    private List<KiWiLiteral> listLiterals(KiWiResource r, String propLabel, Locale loc, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        // TODO: should be implemented as an RDF set or sequence!

        LinkedList<KiWiLiteral> result = new LinkedList<KiWiLiteral>();

        for(KiWiNode node : listOutgoingNodes(r,propLabel, context)) {
            if(node.isLiteral() &&
               ( (((KiWiLiteral)node).getLanguage() == null && loc == null) ||
                 (((KiWiLiteral)node).getLanguage() != null && ((KiWiLiteral)node).getLanguage().equals(loc)) ) ) {
                result.add((KiWiLiteral)node);
            }
        }
        return result;
    }

    /**
     * Generic method to query for literal values related to this resource with the property
     * identified by "propLabel" (SeRQL/SPARQL short or long notation) and the given locale.
     *
     * @param propLabel label of the property; either RDF short form (e.g. "foaf:mbox") or long form (e.g. <http://xmlns.com/foaf/0.1/mbox>)
     * @param loc
     * @return
     * @throws NamespaceResolvingException
     */
    private List<KiWiLiteral> listLiterals(KiWiResource r, String propLabel, Locale loc) throws NamespaceResolvingException, ResourceResolvingException {

        return listLiterals(r, propLabel, loc, null);
    }


    private String _resolvePropLabel(String propLabel) throws NamespaceResolvingException {
        String uri = propLabel;

        // find out which kind of propLabel we got passed
        if(uri.startsWith("<") && uri.endsWith(">")) {
            // uri is a real uri enclosed in < >
            uri = uri.substring(1,uri.length()-1);
        } else if(!uri.contains("://") && uri.contains(":")) {
            // uri is a SeQRQL/SPARQL identifier with abbreviated namespace, we need to lookup the namespace...
            String[] components = uri.split(":");
            if(components.length == 2) {
                String ns_prefix = components[0];
                String ns_local  = components[1];

                KiWiNamespace ns = tripleStore.getNamespaceByPrefix(ns_prefix);
                if(ns == null) {
                    throw new NamespaceResolvingException("The namespace with prefix " + ns_prefix +
                            "could not be resolved. Have you imported the kiwi core ontology?");
                }
                uri = ns.getUri() + ns_local;
            } else {
                log.error("could not properly split property identifier #0, as it contained more than one ':'",uri);
            }
        }
        return uri;
    }

}
